Python(boto3)でS3にデータをファイル保存せず直接アップロードする方法
こんにちは、臼田です。
Pythonを利用してS3にデータをアップロードする際、boto3を利用することになると思いますが、検索するとファイルからアップロードする方法がいっぱい出てきます。
でも、私はスクリプトの中で作成したjsonデータを直接S3に格納したかったんです。
なぜなら、Lambdaで処理したデータをjsonにして格納することが目的だったので、一時的にファイルで保存するなんてことは考えられないからです。
boto3の事、よくわかっていなくてハマってしまったので共有したいと思います。
執筆時のboto3のバージョンは1.4.4です。
PythonはLambda前提の2.7です。
S3へ直接保存する方法
boto3のドキュメントのclass S3.Objectのput()メソッドには、下記のように記載があります。(ver.1.4.4現在)
Request Syntax
response = object.put( ACL='private'|'public-read'|'public-read-write'|'authenticated-read'|'aws-exec-read'|'bucket-owner-read'|'bucket-owner-full-control', Body=b'bytes'|file, ……省略……
これは、Bodyにはfile objectかbytes型を指定すると書かれているように見受けられます。
大方の検索結果には、open()を利用したFileStreamで参考例が上がっていました。
bytesの方ですが、実際にはPython2.7ではbytes型は存在していないので、bytearray型になるかと思います。(私はここでしばらくハマりました)
一見するとjsonをbytearrayに変換して流し込んであげればいいので、下記のように書いてみました。
# test.py import json import boto3 bucket_name = "test-bucket" json_key = "test.json" s3 = boto3.resource('s3') obj = s3.Object(bucket_name,json_key) test_json = {'key': 'value'} r = obj.put(Body = bytearray(json.dumps(test_json))) # get json data print obj.get()['Body'].read()
S3.Object.put()にbytearrayを渡してあげると、下記のように正常にjsonデータが格納されました。
$ python test.py {"key": "value"}
しかし、これは最適な解ではありませんでした。
S3にjsonを渡すときの最適解
うっかりjson.dumps()ではなくdictのままデータを渡したときにそれは起こりました。
botocore.exceptions.ParamValidationError: Parameter validation failed: Invalid type for parameter Body, value: {}, type: <type 'dict'>, valid types: <type 'str'>, <type 'bytearray'>, file-like object
ドキュメントにはなかった<type 'str'>
の文字が…
というわけで、わざわざbytearrayに入れなくても、下記のような形で直接データを送れます。
# test2.py import json import boto3 bucket_name = "test-bucket" json_key = "test.json" s3 = boto3.resource('s3') obj = s3.Object(bucket_name,json_key) test_json = {'key': 'value'} r = obj.put(Body = json.dumps(test_json)) # get json data print obj.get()['Body'].read()
$ python test2.py {"key": "value"}
それでは、よいboto3ライフを